package cn.edu.seu.cose.qol.filter;

import java.io.IOException;
import java.sql.SQLException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import cn.edu.seu.cose.qol.model.bo.AuthorizationBean;
import cn.edu.seu.cose.qol.model.dao.database.account.AdminAccountDataAccess;
import cn.edu.seu.cose.qol.model.po.account.AdminAccount;


import sun.misc.BASE64Decoder;

@WebFilter("/resource/*")
public class ResourceAuthenticationFilter implements Filter {
	
	private static final String AUTH_BEAN_ATTRI = "authBean";
	
	private static final String AUTHORIZATION_HEADER = "authorization";
	
	private static final String BASIC_AUTH_REALM =
			"qol resource authentication";
	
	private static final String CHAR_ENCODING = "utf-8";
	
	private static final String HTTP_METHOD_GET = "GET";
	
	private static final String HTTP_BASIC_AUTH = "Basic";
	
	private static String getAuthenticationType(String authorization)
			throws IOException {
		return authorization.split(" +")[0];
	}
	
	private static String getAuthenticationInfo(String authorization)
			throws IOException {
		return new String(new BASE64Decoder().decodeBuffer(
				authorization.split(" +")[1]));
	}
	
	private static String[] getUsernameAndPasswordArray(String authInfo) {
		return authInfo.split(":");
	}
	
	private static void sendNotAuthorizedError(
			HttpServletResponse httpResponse) throws IOException {
		httpResponse.setHeader("WWW-authenticate",
				"Basic realm=" + "\"" + BASIC_AUTH_REALM + "\"");
		httpResponse.sendError(401, "Unauthorized");
	}
	
	private static boolean authenticate(String username, String password)
			throws SQLException {
		if (username == null || password == null) {
			return false;
		}
		
		AdminAccount adminAccount = new AdminAccount();
		adminAccount.setUsername(username);
		adminAccount.setPassword(password);
		
		AdminAccountDataAccess adminAccountAccess =
				AdminAccountDataAccess.getInstance();
		return adminAccountAccess.authenticateAdminAccount(adminAccount);
	}

	@Override
	public void init(FilterConfig fConfig) throws ServletException {}

	@Override
	public void destroy() {}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain)
					throws IOException, ServletException {
		request.setCharacterEncoding(CHAR_ENCODING);
		response.setCharacterEncoding(CHAR_ENCODING);
		
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		
		String httpMethod = httpRequest.getMethod();
		if (HTTP_METHOD_GET.equals(httpMethod)) {
			chain.doFilter(request, response);
			return;
		}
		
		doAuthentication(httpRequest, httpResponse, chain);
	}
	
	private void doAuthentication(HttpServletRequest request,
			HttpServletResponse response, FilterChain chain)
					throws IOException, ServletException {
		HttpSession session = request.getSession();
		
		Object authObj = session.getAttribute(AUTH_BEAN_ATTRI);
		
		// 若曾被授权
		if (authObj != null) {
			chain.doFilter(request, response);
			return;
		}
		
		// 若未经授权
		String authorization = request.getHeader(AUTHORIZATION_HEADER);
		// 若无认证头
		if (authorization == null || "".equals(authorization)) {
			sendNotAuthorizedError(response);
			return;
		}
		
		String authType = getAuthenticationType(authorization);
		// 若非Basic Auth
		if (!HTTP_BASIC_AUTH.equalsIgnoreCase(authType)) {
			sendNotAuthorizedError(response);
			return;
		}
		
		String authenticationInfo = getAuthenticationInfo(authorization);
		String[] pair = getUsernameAndPasswordArray(authenticationInfo);
		// 若验证对不完整
		if (pair.length < 2) {
			sendNotAuthorizedError(response);
			return;
		}
		
		String username = pair[0];
		String password = pair[1];
		try {
			// 若验证对不匹配
			if (!authenticate(username, password)) {
				sendNotAuthorizedError(response);
				return;
			}
		} catch (SQLException e) {
			// 数据库连接错误
			response.sendError(500);
			e.printStackTrace();
			return;
		}
		
		// 认证成功
		AuthorizationBean authBean = new AuthorizationBean();
		long current = System.currentTimeMillis();
		authBean.setTime(current);
		authBean.setUsername(username);
		
		session.setAttribute(AUTH_BEAN_ATTRI, authBean);
		chain.doFilter(request, response);
	}

}
